﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using Ast.Framework.Environment.Extensions.Compilers;
using Ast.Framework.FileSystems.Paths;
using Ast.Framework.FileSystems.SiteFolders;

namespace Ast.Framework.Environment
{
    public interface IBuildManager : IDependency
    {
        IEnumerable<Assembly> GetReferencedAssemblies();
        bool HasReferencedAssembly(string name);
        Assembly GetReferencedAssembly(string name);
        Assembly GetCompiledAssembly(string path);
    }

    public class DefaultBuildManager : IBuildManager
    {
        private readonly ISiteFolderRoot _siteFolderRoot;
        private readonly IPathProvider _pathProvider;
        private readonly IAssemblyLoader _assemblyLoader;
        private readonly Lazy<IExtensionCompiler> _extensionCompiler;

        private readonly ConcurrentDictionary<string, Assembly> _compiledaAssemblies;

        public DefaultBuildManager(ISiteFolderRoot siteFolderRoot, IPathProvider pathProvider, IAssemblyLoader assemblyLoader, Lazy<IExtensionCompiler> extensionCompiler)
        {
            _siteFolderRoot = siteFolderRoot;
            _pathProvider = pathProvider;
            _assemblyLoader = assemblyLoader;
            _extensionCompiler = extensionCompiler;

            _compiledaAssemblies = new ConcurrentDictionary<string, Assembly>();
        }

        public IEnumerable<Assembly> GetReferencedAssemblies()
        {
            return AppDomain.CurrentDomain.GetAssemblies();
        }

        public bool HasReferencedAssembly(string name)
        {
            var assemblyPath = _pathProvider.Combine(_siteFolderRoot.Path, name + ".dll");
            return _pathProvider.FileExists(assemblyPath);
        }

        public Assembly GetReferencedAssembly(string name)
        {
            return HasReferencedAssembly(name) ? _assemblyLoader.Load(name) : null;
        }

        public Assembly GetCompiledAssembly(string path)
        {
            return _compiledaAssemblies.GetOrAdd(path, s => _extensionCompiler.Value.Compile(new CompileExtensionContext {Path = s}));
        }
    }
}